mod models;

use crate::db;
use crate::db::{IdItem, InitTodoItem, OwnerItem, TodoUpdateItem};
use crate::errors::HandlerError;
use actix_web::web;
use sqlx::PgPool;
use std::result::Result;
use uuid::Uuid;

fn generate_uuid() -> Uuid {
    Uuid::new_v4()
}

impl From<db::TodoItem> for models::TodoItem {
    fn from(db_item: db::TodoItem) -> Self {
        let created_at = db_item
            .created_at
            .expect("created_at 为空")
            .format("%Y-%m-%dT%H:%M:%S%.fZ")
            .to_string();
        let updated_at = db_item
            .updated_at
            .expect("updated_at 为空")
            .format("%Y-%m-%dT%H:%M:%S%.fZ")
            .to_string();
        Self {
            id: db_item.id,
            owner_key: db_item.owner_key,
            title: db_item.title,
            description: db_item.description,
            finished: db_item.finished.unwrap_or_default(),
            created_at,
            updated_at,
        }
    }
}
pub async fn add_todo_item(
    db_pool: web::Data<PgPool>,
    item: web::Json<InitTodoItem>,
) -> Result<String, HandlerError> {
    let mut tx = db_pool.begin().await?;
    let uuid = generate_uuid().to_string();

    match item.exec(&mut tx, &uuid).await {
        Ok(_) => {
            tx.commit().await?;
            Ok(uuid)
        }
        Err(err) => {
            tx.rollback().await?;
            Err(err.into())
        }
    }
}

pub async fn update_todo_item(
    db_pool: web::Data<PgPool>,
    item: web::Json<TodoUpdateItem>,
) -> Result<web::Json<()>, HandlerError> {
    let mut tx = db_pool.begin().await?;

    match item.exec(&mut tx).await {
        Ok(_) => {
            tx.commit().await?;
            Ok(web::Json(()))
        }
        Err(err) => {
            tx.rollback().await?;
            Err(err.into())
        }
    }
}

pub async fn get_todo_items(
    db_pool: web::Data<PgPool>,
    owner_key: web::Path<String>,
) -> Result<web::Json<Vec<models::TodoItem>>, HandlerError> {
    let owner_key = owner_key.into_inner();
    Ok(web::Json(
        OwnerItem { owner_key }
            .exec(&db_pool)
            .await?
            .iter()
            .map(|item| models::TodoItem::from(item.clone()))
            .collect::<Vec<models::TodoItem>>(),
    ))
}

pub async fn get_todo_item_by_id(
    db_pool: web::Data<PgPool>,
    id: web::Path<String>,
) -> Result<web::Json<models::TodoItem>, HandlerError> {
    let id = id.into_inner();

    let result = IdItem { id: id.clone() }.get(&db_pool).await?;
    if let Some(item) = result {
        Ok(web::Json(item.into()))
    } else {
        Err(HandlerError::Msg(format!(
            "无效id：{},未找到对应的数据",
            &id
        )))
    }
}
